package com.sun.electric.compression;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.kohsuke.args4j.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.electric.compression.api.BZIP2OutputStreamJava;

public class Main {

	public static void main(String[] args) {
		Main main = new Main();
		CmdLineParser parser = new CmdLineParser(main);
		try {
			parser.parseArgument(args);
		} catch (CmdLineException ex) {
			parser.printUsage(System.err);
			return;
		}
		main.process();
	}

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Argument(usage = "Files to compress")
	private List<File> inputFiles;

	@Option(name = "-", usage = "Block-size in 100kB ... 900kB")
	private int blockSizeFactor;

	@Option(name = "-p", aliases = "--threads", usage = "Number of threads")
	private int numberOfThreads;

	@Option(name = "-f", aliases = "--force", usage = "Force overwrite of output files")
	private boolean force;

	@Option(name = "-c", aliases = "--stdout", usage = "Write compressed data to system output")
	private boolean stdout;

	public Main() {
		this.inputFiles = new ArrayList<File>();
		this.blockSizeFactor = 9;
		this.numberOfThreads = Runtime.getRuntime().availableProcessors();
	}

	private void process() {
		for (final File file : inputFiles) {
			File outputFile = new File(file.getAbsolutePath() + ".bz2");
			if (!stdout && (!force && outputFile.exists())) {
				logger.error("Output file {} already exists!", outputFile);
				return;
			}
			compressFile(file, outputFile);
		}
	}


	long originalFileLength = 0;
	long startTime;
	long endTime;
	private void compressFile(final File inputFile, final File outputFile) {

	    startTime = System.currentTimeMillis();
		logger.debug("Compressing file {} to {}", inputFile, outputFile);
		InputStream in = null;
		BZIP2OutputStreamJava out = null;
		try {
			in = new FileInputStream(inputFile);
			//we need a BufferedOutputStream for more performance!!
			final OutputStream fos = (stdout) ? System.out : new BufferedOutputStream (new FileOutputStream (outputFile), 524288);

			out = new BZIP2OutputStreamJava(fos, this.blockSizeFactor, this.numberOfThreads);
			logger.info("Using algorithm: {}", "bzip2");

			byte[] a = new byte[524288];
			int length;
			while((length = in.read(a)) > 0) {
				originalFileLength += length;
				out.write(a, 0, length);
			}
		    
		    
		} catch (IOException ex) {
			logger.error("IO exception: ", ex);
		} finally {
			try {
				if (out != null) {
					out.close();
				}
				
				endTime = System.currentTimeMillis();
				
				long ratio = -1;
				long outputSize = outputFile.length();
				if (outputSize > 0) {
					ratio = originalFileLength / outputSize;
				}
				
				logger.debug("Done compressing file {}. \n Compression ratio: {}" +
						"\n Execution time: " + (endTime - startTime), inputFile, ratio);
				if (in != null) {
					in.close();
				}
			} catch (IOException ex) {
				logger.error("IO exception: ", ex);
			}
		}
	}
}
